/*
 * Copyright (C) 2010-2011 ProjectSkyfire <http://www.projectskyfire.org/>
 * 
 * Copyright (C) 2011 ArkCORE <http://www.arkania.net/>
 * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

/* ScriptData
 Name: npc_commandscript
 %Complete: 100
 Comment: All npc related commands
 Category: commandscripts
 EndScriptData */

#include "ScriptMgr.h"
#include "ObjectMgr.h"
#include "Chat.h"
#include "Transport.h"
#include "CreatureGroups.h"
#include "TargetedMovementGenerator.h"                      // for HandleNpcUnFollowCommand
class npc_commandscript: public CommandScript {
public:
	npc_commandscript() :
			CommandScript("npc_commandscript") {
	}

	ChatCommand* GetCommands() const {
		static ChatCommand npcAddCommandTable[] = { { "formation",
				SEC_MODERATOR, false, &HandleNpcAddFormationCommand, "", NULL },
				{ "item", SEC_GAMEMASTER, false, &HandleNpcAddVendorItemCommand,
						"", NULL }, { "move", SEC_GAMEMASTER, false,
						&HandleNpcAddMoveCommand, "", NULL }, { "temp",
						SEC_GAMEMASTER, false, &HandleNpcAddTempSpawnCommand,
						"", NULL },
				//{ TODO: fix or remove this command
				{ "weapon", SEC_ADMINISTRATOR, false,
						&HandleNpcAddWeaponCommand, "", NULL },
				//}
				{ "", SEC_GAMEMASTER, false, &HandleNpcAddCommand, "", NULL }, {
						NULL, 0, false, NULL, "", NULL } };
		static ChatCommand npcDeleteCommandTable[] = { { "item", SEC_GAMEMASTER,
				false, &HandleNpcDeleteVendorItemCommand, "", NULL }, { "",
				SEC_GAMEMASTER, false, &HandleNpcDeleteCommand, "", NULL }, {
				NULL, 0, false, NULL, "", NULL } };
		static ChatCommand npcFollowCommandTable[] = { { "stop", SEC_GAMEMASTER,
				false, &HandleNpcUnFollowCommand, "", NULL }, { "",
				SEC_GAMEMASTER, false, &HandleNpcFollowCommand, "", NULL }, {
				NULL, 0, false, NULL, "", NULL } };
		static ChatCommand npcSetCommandTable[] = { { "allowmove",
				SEC_ADMINISTRATOR, false, &HandleNpcSetAllowMovementCommand, "",
				NULL }, { "deathstate", SEC_GAMEMASTER, false,
				&HandleNpcSetDeathStateCommand, "", NULL }, { "entry",
				SEC_ADMINISTRATOR, false, &HandleNpcSetEntryCommand, "", NULL },
				{ "factionid", SEC_GAMEMASTER, false,
						&HandleNpcSetFactionIdCommand, "", NULL }, { "flag",
						SEC_GAMEMASTER, false, &HandleNpcSetFlagCommand, "",
						NULL }, { "level", SEC_GAMEMASTER, false,
						&HandleNpcSetLevelCommand, "", NULL }, { "link",
						SEC_GAMEMASTER, false, &HandleNpcSetLinkCommand, "",
						NULL }, { "model", SEC_GAMEMASTER, false,
						&HandleNpcSetModelCommand, "", NULL }, { "movetype",
						SEC_GAMEMASTER, false, &HandleNpcSetMoveTypeCommand, "",
						NULL }, { "phase", SEC_GAMEMASTER, false,
						&HandleNpcSetPhaseCommand, "", NULL }, { "spawndist",
						SEC_GAMEMASTER, false, &HandleNpcSetSpawnDistCommand,
						"", NULL }, { "spawntime", SEC_GAMEMASTER, false,
						&HandleNpcSetSpawnTimeCommand, "", NULL },
				//{ TODO: fix or remove these commands
				{ "name", SEC_GAMEMASTER, false, &HandleNpcSetNameCommand, "",
						NULL }, { "subname", SEC_GAMEMASTER, false,
						&HandleNpcSetSubNameCommand, "", NULL },
				//}
				{ NULL, 0, false, NULL, "", NULL } };
		static ChatCommand npcCommandTable[] = { { "info", SEC_ADMINISTRATOR,
				false, &HandleNpcInfoCommand, "", NULL }, { "move",
				SEC_GAMEMASTER, false, &HandleNpcMoveCommand, "", NULL }, {
				"playemote", SEC_ADMINISTRATOR, false,
				&HandleNpcPlayEmoteCommand, "", NULL }, { "say", SEC_MODERATOR,
				false, &HandleNpcSayCommand, "", NULL }, { "textemote",
				SEC_MODERATOR, false, &HandleNpcTextEmoteCommand, "", NULL }, {
				"whisper", SEC_MODERATOR, false, &HandleNpcWhisperCommand, "",
				NULL }, { "yell", SEC_MODERATOR, false, &HandleNpcYellCommand,
				"", NULL }, { "tame", SEC_GAMEMASTER, false,
				&HandleNpcTameCommand, "", NULL }, { "add", SEC_GAMEMASTER,
				false, NULL, "", npcAddCommandTable }, { "delete",
				SEC_GAMEMASTER, false, NULL, "", npcDeleteCommandTable },
				{ "follow", SEC_GAMEMASTER, false, NULL, "",
						npcFollowCommandTable }, { "set", SEC_GAMEMASTER, false,
						NULL, "", npcSetCommandTable }, { NULL, 0, false, NULL,
						"", NULL } };
		static ChatCommand commandTable[] =
				{ { "npc", SEC_MODERATOR, false, NULL, "", npcCommandTable }, {
						NULL, 0, false, NULL, "", NULL } };
		return commandTable;
	}

	//add spawn of creature
	static bool HandleNpcAddCommand(ChatHandler* handler, const char* args) {
		if (!*args)
			return false;
		char* charID = handler->extractKeyFromLink((char*) args,
				"Hcreature_entry");
		if (!charID)
			return false;

		char* team = strtok(NULL, " ");
		int32 teamval = 0;
		if (team) {
			teamval = atoi(team);
		}
		if (teamval < 0) {
			teamval = 0;
		}

		uint32 id = atoi(charID);

		Player *chr = handler->GetSession()->GetPlayer();
		float x = chr->GetPositionX();
		float y = chr->GetPositionY();
		float z = chr->GetPositionZ();
		float o = chr->GetOrientation();
		Map *map = chr->GetMap();

		if (chr->GetTransport()) {
			uint32 tguid = chr->GetTransport()->AddNPCPassenger(0, id,
					chr->GetTransOffsetX(), chr->GetTransOffsetY(),
					chr->GetTransOffsetZ(), chr->GetTransOffsetO());
			if (tguid > 0)
				WorldDatabase.PQuery(
						"INSERT INTO creature_transport (guid, npc_entry, transport_entry,  TransOffsetX, TransOffsetY, TransOffsetZ, TransOffsetO) values (%u, %u, %f, %f, %f, %f, %u)",
						tguid, id, chr->GetTransport()->GetEntry(),
						chr->GetTransOffsetX(), chr->GetTransOffsetY(),
						chr->GetTransOffsetZ(), chr->GetTransOffsetO());

			return true;
		}

		Creature* pCreature = new Creature;
		if (!pCreature->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_UNIT), map, chr->GetPhaseMaskForSpawn(), id, 0, (uint32)teamval, x, y, z, o))
		{
			delete pCreature;
			return false;
		}

		pCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()),
				chr->GetPhaseMaskForSpawn());

		uint32 db_guid = pCreature->GetDBTableGUIDLow();

		// To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells();
		pCreature->LoadFromDB(db_guid, map);

		map->Add(pCreature);
		sObjectMgr->AddCreatureToGrid(db_guid,
				sObjectMgr->GetCreatureData(db_guid));
		return true;
	}

	//add item in vendorlist
	static bool HandleNpcAddVendorItemCommand(ChatHandler* handler,
			const char* args) {
		if (!*args)
			return false;

		char* pitem = handler->extractKeyFromLink((char*) args, "Hitem");
		if (!pitem) {
			handler->SendSysMessage(LANG_COMMAND_NEEDITEMSEND);
			handler->SetSentErrorMessage(true);
			return false;
		}

		uint32 itemId = atol(pitem);

		char* fmaxcount = strtok(NULL, " "); //add maxcount, default: 0
		uint32 maxcount = 0;
		if (fmaxcount)
			maxcount = atol(fmaxcount);

		char* fincrtime = strtok(NULL, " "); //add incrtime, default: 0
		uint32 incrtime = 0;
		if (fincrtime)
			incrtime = atol(fincrtime);

		char* fextendedcost = strtok(NULL, " "); //add ExtendedCost, default: 0
		uint32 extendedcost = fextendedcost ? atol(fextendedcost) : 0;
		Creature* vendor = handler->getSelectedCreature();
		if (!vendor) {
			handler->SendSysMessage(LANG_SELECT_CREATURE);
			handler->SetSentErrorMessage(true);
			return false;
		}

		uint32 vendor_entry = vendor ? vendor->GetEntry() : 0;

		if (!sObjectMgr->IsVendorItemValid(vendor_entry, itemId, maxcount,
				incrtime, extendedcost, handler->GetSession()->GetPlayer())) {
			handler->SetSentErrorMessage(true);
			return false;
		}

		sObjectMgr->AddVendorItem(vendor_entry, itemId, maxcount, incrtime,
				extendedcost);

		ItemPrototype const* pProto = ObjectMgr::GetItemPrototype(itemId);

		handler->PSendSysMessage(LANG_ITEM_ADDED_TO_LIST, itemId, pProto->Name1,
				maxcount, incrtime, extendedcost);
		return true;
	}

	//add move for creature
	static bool HandleNpcAddMoveCommand(ChatHandler* handler,
			const char* args) {
		if (!*args)
			return false;

		char* guid_str = strtok((char*) args, " ");
		char* wait_str = strtok((char*) NULL, " ");

		uint32 lowguid = atoi((char*) guid_str);

		Creature* pCreature = NULL;

		/* FIXME: impossible without entry
		 if (lowguid)
		 pCreature = ObjectAccessor::GetCreature(*handler->GetSession()->GetPlayer(),MAKE_GUID(lowguid,HIGHGUID_UNIT));
		 */

		// attempt check creature existence by DB data
		if (!pCreature) {
			CreatureData const* data = sObjectMgr->GetCreatureData(lowguid);
			if (!data) {
				handler->PSendSysMessage(LANG_COMMAND_CREATGUIDNOTFOUND,
						lowguid);
				handler->SetSentErrorMessage(true);
				return false;
			}
		} else {
			// obtain real GUID for DB operations
			lowguid = pCreature->GetDBTableGUIDLow();
		}

		int wait = wait_str ? atoi(wait_str) : 0;

		if (wait < 0)
			wait = 0;

		//Player* player = handler->GetSession()->GetPlayer();

		//WaypointMgr.AddLastNode(lowguid, player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetOrientation(), wait, 0);

		// update movement type
		WorldDatabase.PExecute(
				"UPDATE creature SET MovementType = '%u' WHERE guid = '%u'",
				WAYPOINT_MOTION_TYPE, lowguid);
		if (pCreature && pCreature->GetWaypointPath()) {
			pCreature->SetDefaultMovementType(WAYPOINT_MOTION_TYPE);
			pCreature->GetMotionMaster()->Initialize();
			if (pCreature->isAlive()) // dead creature will reset movement generator at respawn
			{
				pCreature->setDeathState(JUST_DIED);
				pCreature->Respawn(true);
			}
			pCreature->SaveToDB();
		}

		handler->SendSysMessage(LANG_WAYPOINT_ADDED);

		return true;
	}

	static bool HandleNpcSetAllowMovementCommand(ChatHandler* handler,
			const char* /*args*/) {
		if (sWorld->getAllowMovement()) {
			sWorld->SetAllowMovement(false);
			handler->SendSysMessage(LANG_CREATURE_MOVE_DISABLED);
		} else {
			sWorld->SetAllowMovement(true);
			handler->SendSysMessage(LANG_CREATURE_MOVE_ENABLED);
		}
		return true;
	}

	static bool HandleNpcSetEntryCommand(ChatHandler* handler,
			const char* args) {
		if (!*args)
			return false;

		uint32 newEntryNum = atoi(args);
		if (!newEntryNum)
			return false;

		Unit* unit = handler->getSelectedUnit();
		if (!unit || unit->GetTypeId() != TYPEID_UNIT) {
			handler->SendSysMessage(LANG_SELECT_CREATURE);
			handler->SetSentErrorMessage(true);
			return false;
		}
		Creature* creature = unit->ToCreature();
		if (creature->UpdateEntry(newEntryNum))
			handler->SendSysMessage(LANG_DONE);
		else
			handler->SendSysMessage(LANG_ERROR);
		return true;
	}

	//change level of creature or pet
	static bool HandleNpcSetLevelCommand(ChatHandler* handler,
			const char* args) {
		if (!*args)
			return false;

		uint8 lvl = (uint8) atoi((char*) args);
		if (lvl < 1
				|| lvl > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) + 3) {
			handler->SendSysMessage(LANG_BAD_VALUE);
			handler->SetSentErrorMessage(true);
			return false;
		}

		Creature* pCreature = handler->getSelectedCreature();
		if (!pCreature) {
			handler->SendSysMessage(LANG_SELECT_CREATURE);
			handler->SetSentErrorMessage(true);
			return false;
		}

		if (pCreature->isPet()) {
			if (((Pet*) pCreature)->getPetType() == HUNTER_PET) {
				pCreature->SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP,
						sObjectMgr->GetXPForLevel(lvl) / 4);
				pCreature->SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0);
			}
			((Pet*) pCreature)->GivePetLevel(lvl);
		} else {
			pCreature->SetMaxHealth(100 + 30 * lvl);
			pCreature->SetHealth(100 + 30 * lvl);
			pCreature->SetLevel(lvl);
			pCreature->SaveToDB();
		}

		return true;
	}

	static bool HandleNpcDeleteCommand(ChatHandler* handler, const char* args) {
		Creature* unit = NULL;

		if (*args) {
			// number or [name] Shift-click form |color|Hcreature:creature_guid|h[name]|h|r
			char* cId = handler->extractKeyFromLink((char*) args, "Hcreature");
			if (!cId)
				return false;

			uint32 lowguid = atoi(cId);
			if (!lowguid)
				return false;

			if (CreatureData const* cr_data = sObjectMgr->GetCreatureData(lowguid))
				unit =
						handler->GetSession()->GetPlayer()->GetMap()->GetCreature(
								MAKE_NEW_GUID(lowguid, cr_data->id, HIGHGUID_UNIT));
		} else
			unit = handler->getSelectedCreature();

		if (!unit || unit->isPet() || unit->isTotem()) {
			handler->SendSysMessage(LANG_SELECT_CREATURE);
			handler->SetSentErrorMessage(true);
			return false;
		}

		// Delete the creature
		unit->CombatStop();
		unit->DeleteFromDB();
		unit->AddObjectToRemoveList();

		handler->SendSysMessage(LANG_COMMAND_DELCREATMESSAGE);

		return true;
	}

	//del item from vendor list
	static bool HandleNpcDeleteVendorItemCommand(ChatHandler* handler,
			const char* args) {
		if (!*args)
			return false;

		Creature* vendor = handler->getSelectedCreature();
		if (!vendor || !vendor->isVendor()) {
			handler->SendSysMessage(LANG_COMMAND_VENDORSELECTION);
			handler->SetSentErrorMessage(true);
			return false;
		}

		char* pitem = handler->extractKeyFromLink((char*) args, "Hitem");
		if (!pitem) {
			handler->SendSysMessage(LANG_COMMAND_NEEDITEMSEND);
			handler->SetSentErrorMessage(true);
			return false;
		}
		uint32 itemId = atol(pitem);

		if (!sObjectMgr->RemoveVendorItem(vendor->GetEntry(), itemId)) {
			handler->PSendSysMessage(LANG_ITEM_NOT_IN_LIST, itemId);
			handler->SetSentErrorMessage(true);
			return false;
		}

		ItemPrototype const* pProto = ObjectMgr::GetItemPrototype(itemId);

		handler->PSendSysMessage(LANG_ITEM_DELETED_FROM_LIST, itemId,
				pProto->Name1);
		return true;
	}

	//set faction of creature
	static bool HandleNpcSetFactionIdCommand(ChatHandler* handler,
			const char* args) {
		if (!*args)
			return false;

		uint32 factionId = (uint32) atoi((char*) args);

		if (!sFactionTemplateStore.LookupEntry(factionId)) {
			handler->PSendSysMessage(LANG_WRONG_FACTION, factionId);
			handler->SetSentErrorMessage(true);
			return false;
		}

		Creature* pCreature = handler->getSelectedCreature();

		if (!pCreature) {
			handler->SendSysMessage(LANG_SELECT_CREATURE);
			handler->SetSentErrorMessage(true);
			return false;
		}

		pCreature->setFaction(factionId);

		// faction is set in creature_template - not inside creature

		// update in memory
		if (CreatureInfo const *cinfo = pCreature->GetCreatureInfo()) {
			const_cast<CreatureInfo*>(cinfo)->faction_A = factionId;
			const_cast<CreatureInfo*>(cinfo)->faction_H = factionId;
		}

		// and DB
		WorldDatabase.PExecute(
				"UPDATE creature_template SET faction_A = '%u', faction_H = '%u' WHERE entry = '%u'",
				factionId, factionId, pCreature->GetEntry());

		return true;
	}

	//set npcflag of creature
	static bool HandleNpcSetFlagCommand(ChatHandler* handler,
			const char* args) {
		if (!*args)
			return false;

		uint32 npcFlags = (uint32) atoi((char*) args);

		Creature* pCreature = handler->getSelectedCreature();

		if (!pCreature) {
			handler->SendSysMessage(LANG_SELECT_CREATURE);
			handler->SetSentErrorMessage(true);
			return false;
		}

		pCreature->SetUInt32Value(UNIT_NPC_FLAGS, npcFlags);

		WorldDatabase.PExecute(
				"UPDATE creature_template SET npcflag = '%u' WHERE entry = '%u'",
				npcFlags, pCreature->GetEntry());

		handler->SendSysMessage(LANG_VALUE_SAVED_REJOIN);

		return true;
	}

	//npc follow handling
	static bool HandleNpcFollowCommand(ChatHandler* handler,
			const char* /*args*/) {
		Player *player = handler->GetSession()->GetPlayer();
		Creature *creature = handler->getSelectedCreature();

		if (!creature) {
			handler->PSendSysMessage(LANG_SELECT_CREATURE);
			handler->SetSentErrorMessage(true);
			return false;
		}

		// Follow player - Using pet's default dist and angle
		creature->GetMotionMaster()->MoveFollow(player, PET_FOLLOW_DIST,
				creature->GetFollowAngle());

		handler->PSendSysMessage(LANG_CREATURE_FOLLOW_YOU_NOW,
				creature->GetName());
		return true;
	}

	static bool HandleNpcInfoCommand(ChatHandler* handler,
			const char* /*args*/) {
		Creature* target = handler->getSelectedCreature();

		if (!target) {
			handler->SendSysMessage(LANG_SELECT_CREATURE);
			handler->SetSentErrorMessage(true);
			return false;
		}

		uint32 faction = target->getFaction();
		uint32 npcflags = target->GetUInt32Value(UNIT_NPC_FLAGS);
		uint32 displayid = target->GetDisplayId();
		uint32 nativeid = target->GetNativeDisplayId();
		uint32 Entry = target->GetEntry();
		CreatureInfo const* cInfo = target->GetCreatureInfo();

		int64 curRespawnDelay = target->GetRespawnTimeEx() - time(NULL);
		if (curRespawnDelay < 0)
			curRespawnDelay = 0;
		std::string curRespawnDelayStr = secsToTimeString(
				uint64(curRespawnDelay), true);
		std::string defRespawnDelayStr = secsToTimeString(
				target->GetRespawnDelay(), true);

		handler->PSendSysMessage(LANG_NPCINFO_CHAR, target->GetDBTableGUIDLow(),
				faction, npcflags, Entry, displayid, nativeid);
		handler->PSendSysMessage(LANG_NPCINFO_LEVEL, target->getLevel());
		handler->PSendSysMessage(LANG_NPCINFO_HEALTH, target->GetCreateHealth(),
				target->GetMaxHealth(), target->GetHealth());
		handler->PSendSysMessage(LANG_NPCINFO_FLAGS,
				target->GetUInt32Value(UNIT_FIELD_FLAGS),
				target->GetUInt32Value(UNIT_DYNAMIC_FLAGS),
				target->getFaction());
		handler->PSendSysMessage(LANG_COMMAND_RAWPAWNTIMES,
				defRespawnDelayStr.c_str(), curRespawnDelayStr.c_str());
		handler->PSendSysMessage(LANG_NPCINFO_LOOT, cInfo->lootid,
				cInfo->pickpocketLootId, cInfo->SkinLootId);
		handler->PSendSysMessage(LANG_NPCINFO_DUNGEON_ID,
				target->GetInstanceId());
		handler->PSendSysMessage(LANG_NPCINFO_PHASEMASK,
				target->GetPhaseMask());
		handler->PSendSysMessage(LANG_NPCINFO_ARMOR, target->GetArmor());
		handler->PSendSysMessage(LANG_NPCINFO_POSITION,
				float(target->GetPositionX()), float(target->GetPositionY()),
				float(target->GetPositionZ()));

		if ((npcflags & UNIT_NPC_FLAG_VENDOR)) {
			handler->SendSysMessage(LANG_NPCINFO_VENDOR);
		}
		if ((npcflags & UNIT_NPC_FLAG_TRAINER)) {
			handler->SendSysMessage(LANG_NPCINFO_TRAINER);
		}

		return true;
	}

	//move selected creature
	static bool HandleNpcMoveCommand(ChatHandler* handler, const char* args) {
		uint32 lowguid = 0;

		Creature* pCreature = handler->getSelectedCreature();

		if (!pCreature) {
			// number or [name] Shift-click form |color|Hcreature:creature_guid|h[name]|h|r
			char* cId = handler->extractKeyFromLink((char*) args, "Hcreature");
			if (!cId)
				return false;

			lowguid = atoi(cId);

			/* FIXME: impossible without entry
			 if (lowguid)
			 pCreature = ObjectAccessor::GetCreature(*handler->GetSession()->GetPlayer(),MAKE_GUID(lowguid,HIGHGUID_UNIT));
			 */

			// Attempting creature load from DB data
			if (!pCreature) {
				CreatureData const* data = sObjectMgr->GetCreatureData(lowguid);
				if (!data) {
					handler->PSendSysMessage(LANG_COMMAND_CREATGUIDNOTFOUND,
							lowguid);
					handler->SetSentErrorMessage(true);
					return false;
				}

				uint32 map_id = data->mapid;

				if (handler->GetSession()->GetPlayer()->GetMapId() != map_id) {
					handler->PSendSysMessage(LANG_COMMAND_CREATUREATSAMEMAP,
							lowguid);
					handler->SetSentErrorMessage(true);
					return false;
				}
			} else {
				lowguid = pCreature->GetDBTableGUIDLow();
			}
		} else {
			lowguid = pCreature->GetDBTableGUIDLow();
		}

		float x = handler->GetSession()->GetPlayer()->GetPositionX();
		float y = handler->GetSession()->GetPlayer()->GetPositionY();
		float z = handler->GetSession()->GetPlayer()->GetPositionZ();
		float o = handler->GetSession()->GetPlayer()->GetOrientation();

		if (pCreature) {
			if (CreatureData const* data = sObjectMgr->GetCreatureData(pCreature->GetDBTableGUIDLow())) {
				const_cast<CreatureData*>(data)->posX = x;
				const_cast<CreatureData*>(data)->posY = y;
				const_cast<CreatureData*>(data)->posZ = z;
				const_cast<CreatureData*>(data)->orientation = o;
			}
			pCreature->GetMap()->CreatureRelocation(pCreature, x, y, z, o);
			pCreature->GetMotionMaster()->Initialize();
			if (pCreature->isAlive()) // dead creature will reset movement generator at respawn
			{
				pCreature->setDeathState(JUST_DIED);
				pCreature->Respawn();
			}
		}

		WorldDatabase.PExecute(
				"UPDATE creature SET position_x = '%f', position_y = '%f', position_z = '%f', orientation = '%f' WHERE guid = '%u'",
				x, y, z, o, lowguid);
		handler->PSendSysMessage(LANG_COMMAND_CREATUREMOVED);
		return true;
	}

	//play npc emote
	static bool HandleNpcPlayEmoteCommand(ChatHandler* handler,
			const char* args) {
		uint32 emote = atoi((char*) args);

		Creature* target = handler->getSelectedCreature();
		if (!target) {
			handler->SendSysMessage(LANG_SELECT_CREATURE);
			handler->SetSentErrorMessage(true);
			return false;
		}

		if (target->GetTransport())
			if (target->GetGUIDTransport())
				WorldDatabase.PQuery(
						"UPDATE creature_transport SET emote=%u WHERE transport_entry=%u AND guid=%u",
						emote, target->GetTransport()->GetEntry(),
						target->GetGUIDTransport());

		target->SetUInt32Value(UNIT_NPC_EMOTESTATE, emote);

		return true;
	}

	//set model of creature
	static bool HandleNpcSetModelCommand(ChatHandler* handler,
			const char* args) {
		if (!*args)
			return false;

		uint32 displayId = (uint32) atoi((char*) args);

		Creature *pCreature = handler->getSelectedCreature();

		if (!pCreature || pCreature->isPet()) {
			handler->SendSysMessage(LANG_SELECT_CREATURE);
			handler->SetSentErrorMessage(true);
			return false;
		}

		pCreature->SetDisplayId(displayId);
		pCreature->SetNativeDisplayId(displayId);

		pCreature->SaveToDB();

		return true;
	}

	/**HandleNpcSetMoveTypeCommand
	 * Set the movement type for an NPC.<br/>
	 * <br/>
	 * Valid movement types are:
	 * <ul>
	 * <li> stay - NPC wont move </li>
	 * <li> random - NPC will move randomly according to the spawndist </li>
	 * <li> way - NPC will move with given waypoints set </li>
	 * </ul>
	 * additional parameter: NODEL - so no waypoints are deleted, if you
	 *                       change the movement type
	 */
	static bool HandleNpcSetMoveTypeCommand(ChatHandler* handler,
			const char* args) {
		if (!*args)
			return false;

		// 3 arguments:
		// GUID (optional - you can also select the creature)
		// stay|random|way (determines the kind of movement)
		// NODEL (optional - tells the system NOT to delete any waypoints)
		//        this is very handy if you want to do waypoints, that are
		//        later switched on/off according to special events (like escort
		//        quests, etc)
		char* guid_str = strtok((char*) args, " ");
		char* type_str = strtok((char*) NULL, " ");
		char* dontdel_str = strtok((char*) NULL, " ");

		bool doNotDelete = false;

		if (!guid_str)
			return false;

		uint32 lowguid = 0;
		Creature* pCreature = NULL;

		if (dontdel_str) {
			//sLog->outError("DEBUG: All 3 params are set");

			// All 3 params are set
			// GUID
			// type
			// doNotDEL
			if (stricmp(dontdel_str, "NODEL") == 0) {
				//sLog->outError("DEBUG: doNotDelete = true;");
				doNotDelete = true;
			}
		} else {
			// Only 2 params - but maybe NODEL is set
			if (type_str) {
				sLog->outError("DEBUG: Only 2 params ");
				if (stricmp(type_str, "NODEL") == 0) {
					//sLog->outError("DEBUG: type_str, NODEL ");
					doNotDelete = true;
					type_str = NULL;
				}
			}
		}

		if (!type_str) // case .setmovetype $move_type (with selected creature)
		{
			type_str = guid_str;
			pCreature = handler->getSelectedCreature();
			if (!pCreature || pCreature->isPet())
				return false;
			lowguid = pCreature->GetDBTableGUIDLow();
		} else // case .setmovetype #creature_guid $move_type (with selected creature)
		{
			lowguid = atoi((char*) guid_str);

			/* impossible without entry
			 if (lowguid)
			 pCreature = ObjectAccessor::GetCreature(*handler->GetSession()->GetPlayer(),MAKE_GUID(lowguid,HIGHGUID_UNIT));
			 */

			// attempt check creature existence by DB data
			if (!pCreature) {
				CreatureData const* data = sObjectMgr->GetCreatureData(lowguid);
				if (!data) {
					handler->PSendSysMessage(LANG_COMMAND_CREATGUIDNOTFOUND,
							lowguid);
					handler->SetSentErrorMessage(true);
					return false;
				}
			} else {
				lowguid = pCreature->GetDBTableGUIDLow();
			}
		}

		// now lowguid is low guid really existed creature
		// and pCreature point (maybe) to this creature or NULL

		MovementGeneratorType move_type;

		std::string type = type_str;

		if (type == "stay")
			move_type = IDLE_MOTION_TYPE;
		else if (type == "random")
			move_type = RANDOM_MOTION_TYPE;
		else if (type == "way")
			move_type = WAYPOINT_MOTION_TYPE;
		else
			return false;

		// update movement type
		//if (doNotDelete == false)
		//    WaypointMgr.DeletePath(lowguid);

		if (pCreature) {
			// update movement type
			if (doNotDelete == false)
				pCreature->LoadPath(0);

			pCreature->SetDefaultMovementType(move_type);
			pCreature->GetMotionMaster()->Initialize();
			if (pCreature->isAlive()) // dead creature will reset movement generator at respawn
			{
				pCreature->setDeathState(JUST_DIED);
				pCreature->Respawn();
			}
			pCreature->SaveToDB();
		}
		if (doNotDelete == false) {
			handler->PSendSysMessage(LANG_MOVE_TYPE_SET, type_str);
		} else {
			handler->PSendSysMessage(LANG_MOVE_TYPE_SET_NODEL, type_str);
		}

		return true;
	}

	//npc phasemask handling
	//change phasemask of creature or pet
	static bool HandleNpcSetPhaseCommand(ChatHandler* handler,
			const char* args) {
		if (!*args)
			return false;

		uint32 phasemask = (uint32) atoi((char*) args);
		if (phasemask == 0) {
			handler->SendSysMessage(LANG_BAD_VALUE);
			handler->SetSentErrorMessage(true);
			return false;
		}

		Creature* pCreature = handler->getSelectedCreature();
		if (!pCreature) {
			handler->SendSysMessage(LANG_SELECT_CREATURE);
			handler->SetSentErrorMessage(true);
			return false;
		}

		pCreature->SetPhaseMask(phasemask, true);

		if (!pCreature->isPet())
			pCreature->SaveToDB();

		return true;
	}

	//set spawn dist of creature
	static bool HandleNpcSetSpawnDistCommand(ChatHandler* handler,
			const char* args) {
		if (!*args)
			return false;

		float option = (float) (atof((char*) args));
		if (option < 0.0f) {
			handler->SendSysMessage(LANG_BAD_VALUE);
			return false;
		}

		MovementGeneratorType mtype = IDLE_MOTION_TYPE;
		if (option > 0.0f)
			mtype = RANDOM_MOTION_TYPE;

		Creature *pCreature = handler->getSelectedCreature();
		uint32 u_guidlow = 0;

		if (pCreature)
			u_guidlow = pCreature->GetDBTableGUIDLow();
		else
			return false;

		pCreature->SetRespawnRadius((float) option);
		pCreature->SetDefaultMovementType(mtype);
		pCreature->GetMotionMaster()->Initialize();
		if (pCreature->isAlive()) // dead creature will reset movement generator at respawn
		{
			pCreature->setDeathState(JUST_DIED);
			pCreature->Respawn();
		}

		WorldDatabase.PExecute(
				"UPDATE creature SET spawndist=%f, MovementType=%i WHERE guid=%u",
				option, mtype, u_guidlow);
		handler->PSendSysMessage(LANG_COMMAND_SPAWNDIST, option);
		return true;
	}

	//spawn time handling
	static bool HandleNpcSetSpawnTimeCommand(ChatHandler* handler,
			const char* args) {
		if (!*args)
			return false;

		char* stime = strtok((char*) args, " ");

		if (!stime)
			return false;

		int i_stime = atoi((char*) stime);

		if (i_stime < 0) {
			handler->SendSysMessage(LANG_BAD_VALUE);
			handler->SetSentErrorMessage(true);
			return false;
		}

		Creature *pCreature = handler->getSelectedCreature();
		uint32 u_guidlow = 0;

		if (pCreature)
			u_guidlow = pCreature->GetDBTableGUIDLow();
		else
			return false;

		WorldDatabase.PExecute(
				"UPDATE creature SET spawntimesecs=%i WHERE guid=%u", i_stime,
				u_guidlow);
		pCreature->SetRespawnDelay((uint32) i_stime);
		handler->PSendSysMessage(LANG_COMMAND_SPAWNTIME, i_stime);

		return true;
	}

	static bool HandleNpcSayCommand(ChatHandler* handler, const char* args) {
		if (!*args)
			return false;

		Creature* pCreature = handler->getSelectedCreature();
		if (!pCreature) {
			handler->SendSysMessage(LANG_SELECT_CREATURE);
			handler->SetSentErrorMessage(true);
			return false;
		}

		pCreature->MonsterSay(args, LANG_UNIVERSAL, 0);

		// make some emotes
		char lastchar = args[strlen(args) - 1];
		switch (lastchar) {
		case '?':
			pCreature->HandleEmoteCommand(EMOTE_ONESHOT_QUESTION);
			break;
		case '!':
			pCreature->HandleEmoteCommand(EMOTE_ONESHOT_EXCLAMATION);
			break;
		default:
			pCreature->HandleEmoteCommand(EMOTE_ONESHOT_TALK);
			break;
		}

		return true;
	}

	//show text emote by creature in chat
	static bool HandleNpcTextEmoteCommand(ChatHandler* handler,
			const char* args) {
		if (!*args)
			return false;

		Creature* pCreature = handler->getSelectedCreature();

		if (!pCreature) {
			handler->SendSysMessage(LANG_SELECT_CREATURE);
			handler->SetSentErrorMessage(true);
			return false;
		}

		pCreature->MonsterTextEmote(args, 0);

		return true;
	}

	//npc unfollow handling
	static bool HandleNpcUnFollowCommand(ChatHandler* handler,
			const char* /*args*/) {
		Player *player = handler->GetSession()->GetPlayer();
		Creature *creature = handler->getSelectedCreature();

		if (!creature) {
			handler->PSendSysMessage(LANG_SELECT_CREATURE);
			handler->SetSentErrorMessage(true);
			return false;
		}

		if (/*creature->GetMotionMaster()->empty() ||*/
		creature->GetMotionMaster()->GetCurrentMovementGeneratorType()
				!= TARGETED_MOTION_TYPE) {
			handler->PSendSysMessage(LANG_CREATURE_NOT_FOLLOW_YOU,
					creature->GetName());
			handler->SetSentErrorMessage(true);
			return false;
		}

		TargetedMovementGenerator<Creature> const* mgen =
				static_cast<TargetedMovementGenerator<Creature> const*>((creature->GetMotionMaster()->top()));

		if (mgen->GetTarget() != player) {
			handler->PSendSysMessage(LANG_CREATURE_NOT_FOLLOW_YOU,
					creature->GetName());
			handler->SetSentErrorMessage(true);
			return false;
		}

		// reset movement
		creature->GetMotionMaster()->MovementExpired(true);

		handler->PSendSysMessage(LANG_CREATURE_NOT_FOLLOW_YOU_NOW,
				creature->GetName());
		return true;
	}

	// make npc whisper to player
	static bool HandleNpcWhisperCommand(ChatHandler* handler,
			const char* args) {
		if (!*args)
			return false;

		char* receiver_str = strtok((char*) args, " ");
		char* text = strtok(NULL, "");

		uint64 guid = handler->GetSession()->GetPlayer()->GetSelection();
		Creature* pCreature =
				handler->GetSession()->GetPlayer()->GetMap()->GetCreature(guid);

		if (!pCreature || !receiver_str || !text) {
			return false;
		}

		uint64 receiver_guid = atol(receiver_str);

		// check online security
		if (handler->HasLowerSecurity(sObjectMgr->GetPlayer(receiver_guid), 0))
		return false;

		pCreature->MonsterWhisper(text, receiver_guid);

		return true;
	}

	static bool HandleNpcYellCommand(ChatHandler* handler, const char* args) {
		if (!*args)
			return false;

		Creature* pCreature = handler->getSelectedCreature();
		if (!pCreature) {
			handler->SendSysMessage(LANG_SELECT_CREATURE);
			handler->SetSentErrorMessage(true);
			return false;
		}

		pCreature->MonsterYell(args, LANG_UNIVERSAL, 0);

		// make an emote
		pCreature->HandleEmoteCommand(EMOTE_ONESHOT_SHOUT);

		return true;
	}

	// add creature, temp only
	static bool HandleNpcAddTempSpawnCommand(ChatHandler* handler,
			const char* args) {
		if (!*args)
			return false;
		char* charID = strtok((char*) args, " ");
		if (!charID)
			return false;

		Player *chr = handler->GetSession()->GetPlayer();

		uint32 id = atoi(charID);
		if (!id)
			return false;

		chr->SummonCreature(id, *chr, TEMPSUMMON_CORPSE_DESPAWN, 120);

		return true;
	}

	//npc tame handling
	static bool HandleNpcTameCommand(ChatHandler* handler,
			const char* /*args*/) {
		Creature *creatureTarget = handler->getSelectedCreature();
		if (!creatureTarget || creatureTarget->isPet()) {
			handler->PSendSysMessage(LANG_SELECT_CREATURE);
			handler->SetSentErrorMessage(true);
			return false;
		}

		Player *player = handler->GetSession()->GetPlayer();

		if (player->GetPetGUID()) {
			handler->SendSysMessage(LANG_YOU_ALREADY_HAVE_PET);
			handler->SetSentErrorMessage(true);
			return false;
		}

		CreatureInfo const* cInfo = creatureTarget->GetCreatureInfo();

		if (!cInfo->isTameable(player->CanTameExoticPets())) {
			handler->PSendSysMessage(LANG_CREATURE_NON_TAMEABLE, cInfo->Entry);
			handler->SetSentErrorMessage(true);
			return false;
		}

		// Everything looks OK, create new pet
		Pet* pet = player->CreateTamedPetFrom(creatureTarget);
		if (!pet) {
			handler->PSendSysMessage(LANG_CREATURE_NON_TAMEABLE, cInfo->Entry);
			handler->SetSentErrorMessage(true);
			return false;
		}

		// place pet before player
		float x, y, z;
		player->GetClosePoint(x, y, z, creatureTarget->GetObjectSize(),
				CONTACT_DISTANCE);
		pet->Relocate(x, y, z, M_PI - player->GetOrientation());

		// set pet to defensive mode by default (some classes can't control controlled pets in fact).
		pet->SetReactState(REACT_DEFENSIVE);

		// calculate proper level
		uint8 level =
				(creatureTarget->getLevel() < (player->getLevel() - 5)) ?
						(player->getLevel() - 5) : creatureTarget->getLevel();

		// prepare visual effect for levelup
		pet->SetUInt32Value(UNIT_FIELD_LEVEL, level - 1);

		// add to world
		pet->GetMap()->Add(pet->ToCreature());

		// visual effect for levelup
		pet->SetUInt32Value(UNIT_FIELD_LEVEL, level);

		// caster have pet now
		player->SetMinion(pet, true, PET_SLOT_UNK_SLOT);

		pet->SavePetToDB(PET_SLOT_ACTUAL_PET_SLOT);
		player->PetSpellInitialize();

		return true;
	}

	//npc deathstate handling
	static bool HandleNpcSetDeathStateCommand(ChatHandler* handler,
			const char* args) {
		if (!*args)
			return false;

		Creature* pCreature = handler->getSelectedCreature();
		if (!pCreature || pCreature->isPet()) {
			handler->SendSysMessage(LANG_SELECT_CREATURE);
			handler->SetSentErrorMessage(true);
			return false;
		}

		if (strncmp(args, "on", 3) == 0)
			pCreature->SetDeadByDefault(true);
		else if (strncmp(args, "off", 4) == 0)
			pCreature->SetDeadByDefault(false);
		else {
			handler->SendSysMessage(LANG_USE_BOL);
			handler->SetSentErrorMessage(true);
			return false;
		}

		pCreature->SaveToDB();
		pCreature->Respawn();

		return true;
	}

	static bool HandleNpcAddFormationCommand(ChatHandler* handler,
			const char* args) {
		if (!*args)
			return false;

		uint32 leaderGUID = (uint32) atoi((char*) args);
		Creature *pCreature = handler->getSelectedCreature();

		if (!pCreature || !pCreature->GetDBTableGUIDLow()) {
			handler->SendSysMessage(LANG_SELECT_CREATURE);
			handler->SetSentErrorMessage(true);
			return false;
		}

		uint32 lowguid = pCreature->GetDBTableGUIDLow();
		if (pCreature->GetFormation()) {
			handler->PSendSysMessage(
					"Selected creature is already member of group %u",
					pCreature->GetFormation()->GetId());
			return false;
		}

		if (!lowguid)
			return false;

		Player *chr = handler->GetSession()->GetPlayer();
		FormationInfo *group_member;

		group_member = new FormationInfo;
		group_member->follow_angle = (pCreature->GetAngle(chr)
				- chr->GetOrientation()) * 180 / M_PI;
		group_member->follow_dist = sqrtf(
				pow(chr->GetPositionX() - pCreature->GetPositionX(), int(2))
						+ pow(chr->GetPositionY() - pCreature->GetPositionY(),
								int(2)));
		group_member->leaderGUID = leaderGUID;
		group_member->groupAI = 0;

		CreatureGroupMap[lowguid] = group_member;
		pCreature->SearchFormation();

		WorldDatabase.PExecute(
				"INSERT INTO creature_formations (leaderGUID, memberGUID, dist, angle, groupAI) VALUES ('%u','%u','%f', '%f', '%u')",
				leaderGUID, lowguid, group_member->follow_dist,
				group_member->follow_angle, group_member->groupAI);

		handler->PSendSysMessage(
				"Creature %u added to formation with leader %u", lowguid,
				leaderGUID);

		return true;
	}

	static bool HandleNpcSetLinkCommand(ChatHandler* handler,
			const char* args) {
		if (!*args)
			return false;

		uint32 linkguid = (uint32) atoi((char*) args);

		Creature* pCreature = handler->getSelectedCreature();

		if (!pCreature) {
			handler->SendSysMessage(LANG_SELECT_CREATURE);
			handler->SetSentErrorMessage(true);
			return false;
		}

		if (!pCreature->GetDBTableGUIDLow()) {
			handler->PSendSysMessage(
					"Selected creature %u isn't in creature table",
					pCreature->GetGUIDLow());
			handler->SetSentErrorMessage(true);
			return false;
		}

		if (!sObjectMgr->SetCreatureLinkedRespawn(
				pCreature->GetDBTableGUIDLow(), linkguid)) {
			handler->PSendSysMessage(
					"Selected creature can't link with guid '%u'", linkguid);
			handler->SetSentErrorMessage(true);
			return false;
		}

		handler->PSendSysMessage(
				"LinkGUID '%u' added to creature with DBTableGUID: '%u'",
				linkguid, pCreature->GetDBTableGUIDLow());
		return true;
	}

	//TODO: NpcCommands that need to be fixed :
	static bool HandleNpcAddWeaponCommand(ChatHandler* /*handler*/,
			const char* /*args*/) {
		/*if (!*args)
		 return false;

		 uint64 guid = handler->GetSession()->GetPlayer()->GetSelection();
		 if (guid == 0)
		 {
		 handler->SendSysMessage(LANG_NO_SELECTION);
		 return true;
		 }

		 Creature *pCreature = ObjectAccessor::GetCreature(*handler->GetSession()->GetPlayer(), guid);

		 if (!pCreature)
		 {
		 handler->SendSysMessage(LANG_SELECT_CREATURE);
		 return true;
		 }

		 char* pSlotID = strtok((char*)args, " ");
		 if (!pSlotID)
		 return false;

		 char* pItemID = strtok(NULL, " ");
		 if (!pItemID)
		 return false;

		 uint32 ItemID = atoi(pItemID);
		 uint32 SlotID = atoi(pSlotID);

		 ItemPrototype* tmpItem = ObjectMgr::GetItemPrototype(ItemID);

		 bool added = false;
		 if (tmpItem)
		 {
		 switch(SlotID)
		 {
		 case 1:
		 pCreature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY, ItemID);
		 added = true;
		 break;
		 case 2:
		 pCreature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY_01, ItemID);
		 added = true;
		 break;
		 case 3:
		 pCreature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY_02, ItemID);
		 added = true;
		 break;
		 default:
		 handler->PSendSysMessage(LANG_ITEM_SLOT_NOT_EXIST,SlotID);
		 added = false;
		 break;
		 }

		 if (added)
		 handler->PSendSysMessage(LANG_ITEM_ADDED_TO_SLOT,ItemID,tmpItem->Name1,SlotID);
		 }
		 else
		 {
		 handler->PSendSysMessage(LANG_ITEM_NOT_FOUND,ItemID);
		 return true;
		 }
		 */
		return true;
	}

	static bool HandleNpcSetNameCommand(ChatHandler* /*handler*/,
			const char* /*args*/) {
		/* Temp. disabled
		 if (!*args)
		 return false;

		 if (strlen((char*)args)>75)
		 {
		 handler->PSendSysMessage(LANG_TOO_LONG_NAME, strlen((char*)args)-75);
		 return true;
		 }

		 for (uint8 i = 0; i < strlen(args); ++i)
		 {
		 if (!isalpha(args[i]) && args[i] != ' ')
		 {
		 handler->SendSysMessage(LANG_CHARS_ONLY);
		 return false;
		 }
		 }

		 uint64 guid;
		 guid = handler->GetSession()->GetPlayer()->GetSelection();
		 if (guid == 0)
		 {
		 handler->SendSysMessage(LANG_NO_SELECTION);
		 return true;
		 }

		 Creature* pCreature = ObjectAccessor::GetCreature(*handler->GetSession()->GetPlayer(), guid);

		 if (!pCreature)
		 {
		 handler->SendSysMessage(LANG_SELECT_CREATURE);
		 return true;
		 }

		 pCreature->SetName(args);
		 uint32 idname = sObjectMgr->AddCreatureTemplate(pCreature->GetName());
		 pCreature->SetUInt32Value(OBJECT_FIELD_ENTRY, idname);

		 pCreature->SaveToDB();
		 */

		return true;
	}

	static bool HandleNpcSetSubNameCommand(ChatHandler* /*handler*/,
			const char* /*args*/) {
		/* Temp. disabled

		 if (!*args)
		 args = "";

		 if (strlen((char*)args)>75)
		 {
		 handler->PSendSysMessage(LANG_TOO_LONG_SUBNAME, strlen((char*)args)-75);
		 return true;
		 }

		 for (uint8 i = 0; i < strlen(args); i++)
		 {
		 if (!isalpha(args[i]) && args[i] != ' ')
		 {
		 handler->SendSysMessage(LANG_CHARS_ONLY);
		 return false;
		 }
		 }
		 uint64 guid;
		 guid = handler->GetSession()->GetPlayer()->GetSelection();
		 if (guid == 0)
		 {
		 handler->SendSysMessage(LANG_NO_SELECTION);
		 return true;
		 }

		 Creature* pCreature = ObjectAccessor::GetCreature(*handler->GetSession()->GetPlayer(), guid);

		 if (!pCreature)
		 {
		 handler->SendSysMessage(LANG_SELECT_CREATURE);
		 return true;
		 }

		 uint32 idname = sObjectMgr->AddCreatureSubName(pCreature->GetName(),args,pCreature->GetUInt32Value(UNIT_FIELD_DISPLAYID));
		 pCreature->SetUInt32Value(OBJECT_FIELD_ENTRY, idname);

		 pCreature->SaveToDB();
		 */
		return true;
	}
};

void AddSC_npc_commandscript() {
	new npc_commandscript();
}
